import React, { PropsWithChildren, ReactNode, useMemo, useRef, useState, useCallback } from 'react';
import styled from 'styled-components';
import { color, typography } from '../shared/global';
import axios, { AxiosRequestConfig } from 'axios';
import Button from '../button';
import { Icon } from '../icon';
import { Modal } from '../modal';
import { Progress } from '../progress';
import {
  ImgWrapper,
  ImgCloseBtn,
  ProgressListItem,
  ProgressLi,
  ImgUpload,
  chooseProgressListColor,
  // ProgressListItemName,
  IconSpin,
  btnStyle,
  rotateBtnStyle,
} from './styleComponent';

interface ProgressBar {
  filename: string;
  percent: number;
  status: 'ready' | 'success' | 'failed' | 'upload';
  uid: string;
  size: number;
  raw: File | null;
  cancel?: any;
  img?: string | ArrayBuffer | null;
}

type onProgressType = ((p: number, f: File, i: number) => void) | undefined;

type UploadMode = 'default' | 'img';

export const updateFilelist = (
  setFilst: React.Dispatch<React.SetStateAction<ProgressBar[]>>,
  _file: ProgressBar,
  uploadPartial: Partial<ProgressBar>
) => {
  setFilst(prevList => {
    if (prevList) {
      return prevList.map(v => {
        if (v.uid === _file.uid) {
          return { ...v, ...uploadPartial };
        } else {
          return v;
        }
      });
    } else {
      return prevList;
    }
  });
};

const postData = function (
  file: File,
  filename: string,
  config: Partial<AxiosRequestConfig>,
  i: number,
  onProgress: onProgressType,
  setFilst: React.Dispatch<React.SetStateAction<ProgressBar[]>>,
  successCallback: ((res: any, i: number) => void) | undefined,
  failCallback: ((res: any, i: number) => void) | undefined
) {
  const formData = new FormData();
  formData.append(filename, file);
  const source = axios.CancelToken.source();
  const _file: ProgressBar = {
    filename: file.name,
    percent: 0,
    status: 'ready',
    uid: Date.now() + 'upload',
    size: file.size,
    raw: file,
    cancel: source,
  };

  setFilst(prev => {
    return [_file, ...prev];
  });
  const defaultAxiosConfig: Partial<AxiosRequestConfig> = {
    method: 'post',
    url: 'http://localhost:51111/user/uploadAvatar',
    data: formData,
    headers: {
      'Content-Type': 'multipart/form-data',
    },
    cancelToken: source.token,
    onUploadProgress: e => {
      let percentage = Math.round((e.loaded * 100) / e.total) || 0;
      // 更新上传队列
      updateFilelist(setFilst, _file, { status: 'upload', percent: percentage });
      if (onProgress) {
        onProgress(percentage, file, i);
      }
    },
  };
  const mergeConifg = { ...defaultAxiosConfig, ...config };
  return axios(mergeConifg)
    .then(res => {
      updateFilelist(setFilst, _file, { status: 'success', percent: 100 });
      if (successCallback) {
        successCallback(res, i);
      }
    })
    .catch(err => {
      updateFilelist(setFilst, _file, { status: 'failed', percent: 0 });
      failCallback && failCallback(err, i);
    });
};

const getBase64 = (raw: File, callback: Function) => {
  const reader = new FileReader();
  reader.addEventListener('load', () => {
    callback(reader.result);
  });
  reader.readAsDataURL(raw);
};

type ModalContentType = {
  rotate: number;
  times: number;
  img: HTMLImageElement;
  left: number;
  top: number;
};

const cavasDraw = function (modalContent: ModalContentType, canvas: HTMLCanvasElement) {
  const image = modalContent.img;
  const ctx = canvas.getContext('2d');
  // eslint-disable-next-line no-self-assign
  canvas.height = canvas.height; //清屏
  let imgWidth = image.width;
  let imgHeight = image.height;
  //canvas宽高300,判断图片长宽谁长，取长的
  const times = modalContent.times;
  if (imgWidth > imgHeight) {
    //如果宽比高度大
    let rate = canvas.width / imgWidth;
    imgWidth = canvas.width * times; //让宽度等于canvas宽度
    imgHeight = imgHeight * rate * times; //然后让高度等比缩放
  } else {
    let rate = canvas.height / imgHeight;
    imgHeight = canvas.height * times;
    imgWidth = imgWidth * rate * times;
  }
  //此时，长宽已等比例缩放，算起始点位偏移，起始高度就是canvas高-图片高再除2 宽度同理
  const startX = (canvas.width - imgWidth) / 2;
  const startY = (canvas.height - imgHeight) / 2;
  //旋转操作
  //旋转首先移动原点到图片中心，这里中心是canvas中心,然后再移动回来
  const midX = canvas.width / 2;
  const midY = canvas.height / 2;
  ctx?.translate(midX, midY);

  ctx?.rotate(modalContent.rotate);
  ctx?.drawImage(
    image,
    startX - midX + modalContent.left,
    startY - midY + modalContent.top,
    imgWidth,
    imgHeight
  );
  ctx?.translate(0, 0);
};

const showModalToSlice = function (
  f: File,
  canvasRef: React.RefObject<HTMLCanvasElement>,
  modalContent: ModalContentType
) {
  getBase64(f, (s: string) => {
    const canvas = canvasRef.current;
    if (canvas) {
      modalContent.img.src = s;
      modalContent.img.onload = () => {
        cavasDraw(modalContent, canvas);
      };
    }
  });
};

const resolveFilename = function (uploadFilename: string[] | string, index: number) {
  if (Array.isArray(uploadFilename)) {
    return uploadFilename[index];
  } else {
    return uploadFilename;
  }
};

const resolveBtnLoading = function (flist: ProgressBar[]) {
  return flist.some(v => v.status === 'upload');
};

interface UploadListProps {
  flist: ProgressBar[];
  onRemove: (item: ProgressBar) => void;
}

function UploadList(props: UploadListProps) {
  const { flist, onRemove } = props;
  return (
    <ul>
      {flist.map(item => (
        <ProgressLi key={item.uid}>
          <ProgressListItem>
            <div>{item.filename}</div>
            <div>
              <Button
                style={{ padding: 0, background: 'transparent' }}
                onClick={() => onRemove(item)}
              >
                <Icon icon="close" color={chooseProgressListColor(item.status)} />
              </Button>
            </div>
          </ProgressListItem>
          {['upload', 'ready'].includes(item.status) && <Progress count={item.percent}></Progress>}
        </ProgressLi>
      ))}
    </ul>
  );
}

interface imageListProps extends UploadListProps {
  setFlist: React.Dispatch<React.SetStateAction<ProgressBar[]>>;
}

export function ImageList(props: imageListProps) {
  const { flist, onRemove, setFlist } = props;

  useMemo(() => {
    if (flist) {
      flist.forEach(item => {
        if (item.raw && !item.img) {
          getBase64(item.raw, (e: string) => {
            updateFilelist(setFlist, item, {
              img: e || 'error',
            });
          });
        }
      });
    }
  }, [flist, setFlist]);

  return (
    <div>
      {flist.map(item => (
        <span key={item.uid}>
          <ImgWrapper>
            {item.status === 'success' && <img src={item.img as string} alt="upload img"></img>}
            {(item.status === 'ready' || item.status === 'upload') && (
              <IconSpin>
                <Icon icon="sync" color={color.warning}></Icon>
              </IconSpin>
            )}
            {item.status === 'failed' && <Icon icon="photo" color={color.negative}></Icon>}
            <ImgCloseBtn className="closebtn" onClick={() => onRemove(item)}>
              <Icon icon="trash" color={color.light}></Icon>
            </ImgCloseBtn>
          </ImgWrapper>
        </span>
      ))}
    </div>
  );
}

type UploadProps = {
  /** 上传字段名 */
  uploadFilename: string[] | string;
  /** 发送设置 */
  axiosConfig?: Partial<AxiosRequestConfig>;
  /** 获取进度 */
  onProgress?: onProgressType;
  /** 成功回调 */
  successCallback?: ((res: any, i: number) => void) | undefined;
  /** 失败回调 */
  failCallback?: ((res: any, i: number) => void) | undefined;
  /** 上传列表初始值 */
  defaultProgressBar?: ProgressBar[];
  /**上传前校验 */
  beforeUpload?: (f: File, i: number) => boolean | Promise<File>;
  /** 上传靓装模式 */
  uploadMode?: UploadMode;
  /** 是否开启进度列表 */
  progress?: boolean;
  /** 自定义删除 */
  onRemoveCallback?: (f: ProgressBar) => void;
  customRemove?: (
    file: ProgressBar,
    setFlist: React.Dispatch<React.SetStateAction<ProgressBar[]>>
  ) => void;
  /** 允许上传最大容量 */
  max?: number;
  /** accept属性 */
  accept?: string;
  /** input的multiple属性   multiple为true和max冲突 */
  multiple?: boolean;
  /** 用户自定义按钮 */
  customBtn?: ReactNode;
  /** 是否裁剪 */
  slice?: boolean;
};

export function Upload(props: PropsWithChildren<UploadProps>) {
  const {
    axiosConfig,
    onProgress,
    defaultProgressBar,
    uploadFilename,
    successCallback,
    failCallback,
    beforeUpload,
    uploadMode,
    onRemoveCallback,
    customRemove,
    max,
    accept,
    multiple,
    customBtn,
    slice,
  } = props;

  const [flist, setFlist] = useState<ProgressBar[]>(defaultProgressBar || []);
  const inputRef = useRef<HTMLInputElement>(null);
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const [modalOpen, setModalOpen] = useState(false);
  const [modalContent, setModalContent] = useState<ModalContentType>({
    rotate: 0,
    times: 1,
    img: new Image(),
    left: 0,
    top: 0,
  });
  const [mouseActive, setMouseActive] = useState(false);
  const [startXY, setStartXY] = useState({ X: 0, Y: 0 });
  const [rescallback, setResCallback] = useState<{ restfn: Function }>({
    restfn: () => {},
  });

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (!e.target.files) return;
    if (e.target.files && e.target.files.length <= 0) return;
    let filelist = Array.from(e.target.files);

    filelist.forEach((f, i) => {
      const restfn = (f: File) => {
        if (beforeUpload) {
          const p = beforeUpload(f, i);
          if (p instanceof Promise) {
            p.then((res: File) => {
              postData(
                f,
                resolveFilename(uploadFilename, i),
                axiosConfig!,
                i,
                onProgress,
                setFlist,
                successCallback,
                failCallback
              );
            });
          } else {
            if (p) {
              // 返回false不执行
              postData(
                f,
                resolveFilename(uploadFilename, i),
                axiosConfig!,
                i,
                onProgress,
                setFlist,
                successCallback,
                failCallback
              );
            }
          }
          return;
        }
        postData(
          f,
          resolveFilename(uploadFilename, i),
          axiosConfig!,
          i,
          onProgress,
          setFlist,
          successCallback,
          failCallback
        );
      };

      setResCallback({ restfn });
      if (showSlice) {
        setModalOpen(true);
        showModalToSlice(f, canvasRef, modalContent);
      } else {
        restfn(f);
      }
    });
  };

  const handleClick = () => {
    inputRef.current?.click();
  };

  const onRemove = useCallback(
    (file: ProgressBar) => {
      if (customRemove) {
        customRemove(file, setFlist);
      } else {
        setFlist(prev => {
          return prev.filter(item => {
            if (item.uid === file.uid && item.status === 'upload' && item.cancel) {
              item.cancel.cancel();
            }
            return item.uid !== file.uid;
          });
        });
      }
      if (onRemoveCallback) {
        onRemoveCallback(file);
      }
    },
    [customRemove, onRemoveCallback]
  );

  /**超出限定长度不展示上传按钮 */
  const shouldShow = useMemo(() => {
    if (max !== undefined) {
      return flist.length < max;
    } else {
      return true;
    }
  }, [max, flist]);

  /**  是否显示裁剪 相当于vue的computed属性 */
  const showSlice = useMemo(() => {
    if (!multiple && uploadMode === 'img' && slice) {
      return true;
    }
    return false;
  }, [multiple, uploadMode, slice]);

  /** canvas触摸事件 */
  const handleMouseDown = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
    setMouseActive(true);
    setStartXY({
      X: e.clientX - modalContent.left,
      Y: e.clientY - modalContent.top,
    });
  };
  const handleMouseMove = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
    if (mouseActive) {
      let diffX = e.clientX - startXY.X;
      let diffY = e.clientY - startXY.Y;
      let newContent = { ...modalContent, left: diffX, top: diffY };
      setModalContent(newContent);
      cavasDraw(newContent, canvasRef.current!);
    }
  };
  const handleMouseUp = () => {
    setMouseActive(false);
  };
  const handleMouseLeave = () => {
    setMouseActive(false);
  };

  return (
    <div>
      <input
        ref={inputRef}
        type="file"
        onChange={handleChange}
        style={{ display: 'none' }}
        value=""
        accept={accept}
        multiple={multiple}
      />
      {uploadMode === 'default' && (
        <>
          {shouldShow && customBtn ? (
            customBtn
          ) : (
            <Button
              onClick={handleClick}
              isLoading={resolveBtnLoading(flist)}
              loadingText="上传中..."
            >
              upload
            </Button>
          )}
          <UploadList flist={flist} onRemove={onRemove} />
        </>
      )}
      {uploadMode === 'img' && (
        <>
          {shouldShow && (
            <ImgUpload onClick={handleClick}>
              <Icon icon="plus"></Icon>
            </ImgUpload>
          )}

          <ImageList flist={flist} setFlist={setFlist} onRemove={onRemove} />
        </>
      )}

      <Modal
        title="图片裁剪"
        callback={(v: boolean) => {
          if (v) {
            //如果取消，不执行后续上传
            canvasRef.current!.toBlob(function (blob) {
              if (rescallback.restfn) rescallback.restfn(blob);
            });
          }
          //清除旋转和倍数
          setModalContent({ ...modalContent, rotate: 0, times: 1 });
        }}
        maskClose={false}
        closeButton={false}
        visible={modalOpen}
        parentSetState={setModalOpen}
        mask={true}
        style={{ width: '500px' }}
        confirm={true}
      >
        <div onMouseDown={handleMouseDown} onMouseMove={handleMouseMove} onMouseUp={handleMouseUp}>
          <canvas
            width={300}
            height={300}
            style={{ width: '100%', height: '100%', border: '1px dashed #ff4785' }}
            ref={canvasRef}
          >
            　您的浏览器不支持Canvas
          </canvas>
        </div>
        <div style={{ marginTop: '10px' }}>
          <Button
            appearance="primary"
            style={btnStyle}
            onClick={() => {
              let newContent = {
                ...modalContent,
                ...{ times: modalContent.times + 0.1 },
              };
              setModalContent(newContent);
              cavasDraw(newContent, canvasRef.current!);
            }}
          >
            <Icon icon="zoom" color={color.light}></Icon>
          </Button>
          <Button
            appearance="primary"
            style={btnStyle}
            onClick={() => {
              let newContent = {
                ...modalContent,
                ...{ times: modalContent.times - 0.1 },
              };
              setModalContent(newContent);
              cavasDraw(newContent, canvasRef.current!);
            }}
          >
            <Icon icon="zoomout" color={color.light}></Icon>
          </Button>
          <Button
            appearance="primary"
            style={btnStyle}
            onClick={() => {
              let newContent = {
                ...modalContent,
                ...{ rotate: modalContent.rotate - 0.1 },
              };
              setModalContent(newContent);
              cavasDraw(newContent, canvasRef.current!);
            }}
          >
            <Icon icon="undo" color={color.light}></Icon>
          </Button>
          <Button
            appearance="primary"
            style={rotateBtnStyle}
            onClick={() => {
              let newContent = {
                ...modalContent,
                ...{ rotate: modalContent.rotate + 0.1 },
              };
              setModalContent(newContent);
              cavasDraw(newContent, canvasRef.current!);
            }}
          >
            <Icon icon="undo" color={color.light}></Icon>
          </Button>
          <Button
            appearance="primary"
            style={btnStyle}
            onClick={() => {
              let newContent = {
                ...modalContent,
                rotate: 0,
                times: 1,
                left: 0,
                top: 0,
              };
              setModalContent(newContent);
              cavasDraw(newContent, canvasRef.current!);
            }}
          >
            <Icon icon="zoomreset" color={color.light}></Icon>
          </Button>
        </div>
      </Modal>
    </div>
  );
}

Upload.defaultProps = {
  axiosConfig: {},
  uploadFilename: 'avatar',
  uploadMode: 'img',
  successCallback: () => console.log('上传成功'),
  failCallbakc: () => console.log('上传失败'),
};
